package com.mcxx.ansync.listener;

import com.alibaba.fastjson.JSONObject;
import com.mcxx.ansync.annotation.BizEvent;
import com.mcxx.ansync.event.EvtResultRecordEvent;
import com.mcxx.modules.system.dto.EventResultDTO;
import com.mcxx.util.SignUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.util.Date;



@Slf4j
@Aspect
@Component
public class BizEventListenerAspect {
    private final static Logger logger = LoggerFactory.getLogger(BizEventListenerAspect.class);
    private ThreadLocal<Long> startTime = new ThreadLocal<>();
    private EventResultDTO eventResultDTO = new EventResultDTO();


    @Autowired
    private ApplicationContext applicationContext;


    @Pointcut("@annotation(com.mcxx.ansync.annotation.BizEvent)")
    public void bizEventAspect() {
    }


    @Before(value = "bizEventAspect()")
    public void recordEvent(JoinPoint joinPoint) throws Throwable {
        startTime.set(System.currentTimeMillis());
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        BizEvent bizEvent = method.getAnnotation(BizEvent.class);
        String target = joinPoint.getTarget().getClass().getName();
        Object[] argList = joinPoint.getArgs();
        String args = JSONObject.toJSONString(argList[0]);
        JSONObject json = JSONObject.parseObject(args);
        String argument = json.values().toArray()[0].toString();
        eventResultDTO.setSignature(SignUtil.signature(argument));
        eventResultDTO.setEvent(method.getParameterTypes()[0].getName());
        eventResultDTO.setStartTime(new Date());
        eventResultDTO.setName(bizEvent.value());
        eventResultDTO.setListener(target);
        eventResultDTO.setArguments(argument);
    }


    @AfterReturning(returning = "ret", pointcut = "bizEventAspect()")
    public void doAfterReturning(Object ret) {
        long execTime = System.currentTimeMillis() - startTime.get();
        eventResultDTO.setStatus(1);
        eventResultDTO.setFinishTime(new Date());
        eventResultDTO.setExecTime(execTime);
        applicationContext.publishEvent(new EvtResultRecordEvent(this, eventResultDTO));
    }


    @AfterThrowing(pointcut = "bizEventAspect()", throwing = "e")
    public void doAfterThrowable(Throwable e) {
        long execTime = System.currentTimeMillis() - startTime.get();
        eventResultDTO.setStatus(2);
        eventResultDTO.setException(exception(e));
        eventResultDTO.setFinishTime(new Date());
        eventResultDTO.setExecTime(execTime);
        applicationContext.publishEvent(new EvtResultRecordEvent(this, eventResultDTO));
    }


    public static String exception(Throwable t) {
        if (t == null) {
            return null;
        }
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            t.printStackTrace(new PrintStream(baos));
        } finally {
            try {
                baos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return baos.toString();
    }
}
